SEP 8 -- 日志打印规范
Head
- Author: larry
- Status: active
- Type: Standards
- Created: 2017-09-06
摘要
日志的目的主要有两种:排查故障,数据分析。为了更好的完成这些目的,对日志做统一的规范。
修改历史
v1
初始版本
v2
-
access
字段顺序做了微调,cms_key放到了后面 增加输入输出长度字段 transaction_id改名为trace_id,可以由业务代码自行设置 增加extra_value字段,做一些临时存放需求。 username前加userid
-
app
transaction_id改名为trace_id,可以由业务代码自行设置 过长的返回增加"..."后缀 http_content_length改为param_string_length 增加userid cms_key移动位置
-
error
error日志包含warning
-
滚动
删除历史过久的日志、设置固定日期数
日志类型
常见日志类型有以下几种。
- access_log:访问日志。一次请求一条日志,主要目的是为了数据分析。
- app_log:应用日志。一次请求多条日志,主要目的就是排查故障。
- sys_log:系统日志。通常指的是运行应用程序的容器本身的日志,比如uwsgi,nginx等等。
- error_log:错误日志。app_log中一定有错误日志信息,通常还会单独有一个错误日志文件,便于集中查看错误量。warning也放入error中。
- 其他:根据需要还可以有其他各种日志。
我们的系统至少应该有前四种。
access_log(访问日志)
访问日志的主要目的是为了后期的数据分析,所以在设计上尽量格式通用,字段丰富。
Common Log Format
日志使用CLF格式(Common Log Format)。CLF是目前目前主流的访问日志格式,apache和nginx以及其他web服务器都使用这种,有大量第三方日志分析工具都支持这种格式,使用这种格式便于后续的数据分析。
- 一行一条日志。
- 一条日志由多个字段组成,字段间用space或者tab分割,允许多个。
- 字段可以由双引号(")包裹起来,应对本身有空格的字段,或者有些特殊字段。
- 特殊符号转义。'" ' -> '"\ ','\' -> '\\'。空格包含space和tab两种。
- 如果字段没有值或者无意义,用'-'。
日志字段选取
字段的选取上,尽量全面。包含几类。
- 头部:系统公共字段,业务公共字段,版本。
- 输入:原始输入信息,未经任何转换的格式
- 输出:原始输出信息
- 其他:中间统计变量,或者其他必要信息
格式定义
v1: [time_local version] request_id processing_time_ms group_id cms_key station_id kid sid username transaction_id "request_path" request_method "param_string_striped_by_max_length" status ret_code "ret_msg" "$ret_body_striped_by_max_length"
v2: [time_local version] request_id processing_time_ms group_id station_id kid sid cms_key userid username trace_id "request_path" request_method param_string_length "param_string_striped_by_max_length" status ret_code "ret_msg" ret_body_length "$ret_body_striped_by_max_length" "extra_value"
v3: [time_local version request_id return_code group_id ] { "http": { "request_method": "request_uri": "status": }, "ctx": { "request_id": "group_id": "station_id": "kid": "sid": "cms_key": "userid": "username": "request_time": "trace_id": }, "biz": { "response_code": "response_msg": "request_data": { "text": "len": "file_len": "file_ref": } "response_data": { "text": "len": } } }
说明
- version:1开始,每次格式变化+1,要保留历史格式文档。
- request_id:请求唯一id,如果http的X-Guanmai-Request-Id头字段有值就用,没有就自己生成一个,规则:md5(uuid)。
- X-Guanmai-Request-Id:特别定义这样一个头字段,用来在串行请求中传递请求唯一id,以便把请求串起来。
- param_string_striped_by_max_length这种字段,最长设置一个配置值,默认到512字节。
- extra_value: 增加了一个额外的备用字段,便于做一些临时存放需求。
app_log(应用日志)
日常的应用日志,打印内容最全面。
格式定义
基本格式:[日志头部]:日志内容(限制最长)...
日志头部:
v1: [time_local log_level request_id group_id cms_key station_id kid sid username transaction_id]
v2: [time_local log_level request_id group_id station_id kid sid cms_key userid username trace_id]
日志内容:每个请求,至少会打印两行日志:输入和输出。当然,其他内容根据需要打印。
日志内容打印建议
-
输入
在框架中打印。格式:request_path,request_method,param_string_length,param_string
这里的param_string,如果是get,就是原始的query_string。如果是post,就是body的内容,尽量保持原样。
-
输出
在框架中打印。格式:ret_body_length,ret_body_string
-
其他日志
所有与其他模块的交互需要打印日志:数据库,redis,其他微服务
数据库操作/redis操作:需要打印执行的sql语句,或者mongo的请求和响应参数。如果不能确认在执行后能安全打印,那么执行前后各打印一条。 如果请求和响应的数据量太大,那么最好在打印内容中,打印数据长度和数据的摘要,以备排查故障。数据太长了会被统一的日志配置给截断,打出来也显示不完整。
分支判断与流程走向
能从输入参数简单推断出的逻辑流程、可以不打印。如果是要用第三方系统查询结果,配合其他数据做复杂结算才能推断出流程分支的,建议把作为分支流程判断依据的计算结果打印出来。因为实际排查问题的时候,人工很难做出复杂的计算,也就很难判断分支走向了。
debug日志
前面说的日志都是info,自己开发中需要的日志可以打印的更详细。debug日志是随时可以删除的日志。
其他 其他认为需要的日志。如果不知道怎么选择,可以观察debug联调时候打印过的日志,从中发现一些适合变为info的。
-
逗号分割
日志内容部分用逗号分割,不是空格。因为这里是随意打印,空格是值的可能性很大,所以不要用空格做分割,避免误判。
-
trace_id
trace_id是留给业务自行填入的一个字段,比如订单类接口,业务可以把订单号填进去,便于搜索日志。
sys_log(系统日志)
在我们的系统中是uwsgi,可以不用改名,就用uswgi.log。
error_log(错误日志)
在app_log之外,所有的exception再单独打印一个日志,格式与app_log相同。
请求唯一ID(request-id)
在http报文中增加了一个head:X-Guanmai-Request-Id,标示一个请求的请求唯一id。
nginx配置
为了把nginx的access_log和我们自己的access_log对应起来,同一个请求,在两个日志中的记录应该用相同的request-id。
在nginx log配置中,增加$request_id变量。因为这个变量在1.11以上版本才支持,所以必须先升级nginx到最新。
nginx转给uwsgi的请求中,加入一个头字段,X-Guanmai-Request-Id=$request_id
微服务调用rpcclient
增加头字段,X-Guanmai-Request-Id=request_id